﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

using Microsoft.Kinect;
using System.Speech.Synthesis;
using MySql.Data.MySqlClient;   //引用MySql
using System.Collections;   //导入动态数组ArrayList的命名空间
using System.Windows.Navigation; 
namespace BodyTrain
{
    /// <summary>
    /// HighKnee.xaml 的交互逻辑
    /// </summary>
    public partial class HighKnee : Page
    {
        #region 定义私有属性
        //Kinect相关变量
        KinectSensor kinectSensor = null;
        CoordinateMapper coordinateMapper = null;
        MultiSourceFrameReader reader;
        IList<Body> bodies;
        CameraMode _mode = CameraMode.Color;
        //姿势
        private Pose startPose; //开始姿势
        private Pose endPose; //结束姿势
        private Pose switchPose; //切换到动作切换界面的姿势
        //标志位
        bool start = false; //高抬腿Knee函数的开始标志位
        bool countLeft = false; //避免左膝抬起重复计数
        bool countRight = false;    //避免右膝抬起重复计数 
        //左右脚迈出次数统计
        int leftNumber = 0;    //左膝抬起次数
        int rightNumber = 0;   //右膝抬起次数
        //运动时间节点
        bool timeRecord = false;//开始时，只记录一次开始时间
        DateTime startTime = DateTime.MinValue;
        DateTime endTime = DateTime.MinValue;
        //记录T姿势时，左右膝盖数据
        Joint startKneeLeft;
        Joint startKneeRight;

        //语音
        bool soundStart = false; //检测的开始姿势，语音提示一次
        bool soundEnd = false; //检测到结束姿势，语音提示一次
        static SpeechSynthesizer speaker1 = new SpeechSynthesizer();//语音定义
        //只处理一个Body数据
        ulong bodyId = 666; //初始化要跟踪目标的bodyId。不能初始化为0的原因是，当检测到一个人时，BodyCount为6，多余的5个body相应的Id为0。有效的Id是个17位的正整数

        #endregion


        public HighKnee()
        {
            InitializeComponent();//初始化窗口

            PopulatePoseLibrary();
        }


        #region 姿势判断相关函数
        private void PopulatePoseLibrary()//姿势库
        {
            //开始测距 Pose - 伸开双臂
            this.startPose = new Pose();
            this.startPose.Title = "伸开双臂(Start Messure)";
            this.startPose.Angles = new PoseAngle[4];
            this.startPose.Angles[0] = new PoseAngle(JointType.ShoulderLeft, JointType.ElbowLeft, 180, 20);
            this.startPose.Angles[1] = new PoseAngle(JointType.ElbowLeft, JointType.WristLeft, 180, 20);
            this.startPose.Angles[2] = new PoseAngle(JointType.ShoulderRight, JointType.ElbowRight, 0, 20);
            this.startPose.Angles[3] = new PoseAngle(JointType.ElbowRight, JointType.WristRight, 0, 20);

            //结束测量姿势 -举起手来 Both Hands Up
            this.endPose = new Pose();
            this.endPose.Title = "双手前臂举起(Forearms Up)";
            this.endPose.Angles = new PoseAngle[4];
            this.endPose.Angles[0] = new PoseAngle(JointType.ShoulderLeft, JointType.ElbowLeft, 180, 20);
            this.endPose.Angles[1] = new PoseAngle(JointType.ElbowLeft, JointType.WristLeft, 90, 20);
            this.endPose.Angles[2] = new PoseAngle(JointType.ShoulderRight, JointType.ElbowRight, 0, 20);
            this.endPose.Angles[3] = new PoseAngle(JointType.ElbowRight, JointType.WristRight, 90, 20);

            //切换界面姿势-双手向前平举，再配合骨骼点判断手臂前伸
            this.switchPose = new Pose();
            this.switchPose.Title = "双手向前平举(Straighten Arms Forward)";
            this.switchPose.Angles = new PoseAngle[4];
            this.switchPose.Angles[0] = new PoseAngle(JointType.ShoulderLeft, JointType.ElbowLeft, 180, 20);
            this.switchPose.Angles[1] = new PoseAngle(JointType.ElbowLeft, JointType.WristLeft, 180, 20);
            this.switchPose.Angles[2] = new PoseAngle(JointType.ShoulderRight, JointType.ElbowRight, 0, 20);
            this.switchPose.Angles[3] = new PoseAngle(JointType.ElbowRight, JointType.WristRight, 90, 20);
        }
        private void processGesture(Body body, Dictionary<JointType, Point> jointPoints)//姿势匹配
        {
            if (IsPose(body, this.startPose, jointPoints))      //检测到开始姿势，则开始
            {
                start = true;
                remind1.Text = "测量开始";

                if (!soundStart) //仅播放一次声音
                {
                    speaker1.SpeakAsync("测量开始");
                    soundStart = true; //已经播放一次声音了
                    soundEnd = false; //可以播放测量结束语音提示
                }
                if (timeRecord == false) //仅记录第一次开始时间
                {
                    timeRecord = true;
                    startTime = DateTime.Now;
                }

                startKneeLeft = body.Joints[JointType.KneeLeft]; //记录开始姿势时，左右膝盖的骨骼数据
                startKneeRight = body.Joints[JointType.KneeRight];
            }

            if (IsPose(body, endPose, jointPoints) && (body.Joints[JointType.WristLeft].Position.Y > body.Joints[JointType.ShoulderLeft].Position.Y)) //检测到结束姿势，测量结束
            {
                start = false;
                remind1.Text = "测量结束";
                if (!soundEnd) //仅播放一次声音
                {
                    speaker1.SpeakAsync("测量结束");
                    soundEnd = true; //已经播放一次声音了
                    soundStart = false; //可以播放测量开始语音提示
                }
            }
        }
        //判断是否和指定的姿势匹配
        private bool IsPose(Body body, Pose pose, IDictionary<JointType, Point> jointPoints)
        {
            bool isPose = true;
            double angle;
            double poseAngle;
            double poseThreshold;
            double loAngle;
            double hiAngle;

            for (int i = 0; i < pose.Angles.Length && isPose; i++)
            {
                poseAngle = pose.Angles[i].Angle;
                poseThreshold = pose.Angles[i].Threshold;
                angle = GetJointAngle(jointPoints, pose.Angles[i].CenterJoint, pose.Angles[i].AngleJoint);

                hiAngle = poseAngle + poseThreshold;
                loAngle = poseAngle - poseThreshold;

                if (hiAngle >= 360 || loAngle < 0)
                {
                    loAngle = (loAngle < 0) ? 360 + loAngle : loAngle;
                    hiAngle = hiAngle % 360;

                    isPose = !(loAngle > angle && angle > hiAngle);
                }
                else
                {
                    isPose = (loAngle <= angle && hiAngle >= angle);
                }
            }
            return isPose;
        }

        //获取骨骼点角度
        private double GetJointAngle(IDictionary<JointType, Point> jointPoints, JointType centerJoint, JointType angleJoint)
        {
            Point primaryPoint = jointPoints[centerJoint];
            Point anglePoint = jointPoints[angleJoint];
            Point x = new Point(primaryPoint.X + anglePoint.X, primaryPoint.Y);

            double a;
            double b;
            double c;

            a = Math.Sqrt(Math.Pow(primaryPoint.X - anglePoint.X, 2) + Math.Pow(primaryPoint.Y - anglePoint.Y, 2));
            b = anglePoint.X;
            c = Math.Sqrt(Math.Pow(anglePoint.X - x.X, 2) + Math.Pow(anglePoint.Y - x.Y, 2));

            double angleRad = Math.Acos((a * a + b * b - c * c) / (2 * a * b));
            double angleDeg = angleRad * 180 / Math.PI;

            if (primaryPoint.Y < anglePoint.Y)
            {
                angleDeg = 360 - angleDeg;
            }
            return angleDeg;
        }

        #endregion


        #region Kinect相关函数
        //相机模式，彩色、深度、红外
        private enum CameraMode
        {
            Color,
            Depth,
            Infrared
        }

        private void Page_Loaded(object sender, RoutedEventArgs e)
        {
            kinectSensor = KinectSensor.GetDefault();
            coordinateMapper = kinectSensor.CoordinateMapper;
            if (kinectSensor != null)
            {
                kinectSensor.Open();
                this.reader = kinectSensor.OpenMultiSourceFrameReader(FrameSourceTypes.Color | FrameSourceTypes.Depth | FrameSourceTypes.Infrared | FrameSourceTypes.Body);
                this.reader.MultiSourceFrameArrived += reader_MultiSourceFrameArrived;
            }
            //语音设置
            speaker1.SelectVoiceByHints(VoiceGender.Female, VoiceAge.Adult, 2, System.Globalization.CultureInfo.CurrentCulture);
            speaker1.Rate = -2;
            speaker1.Volume = 100;

            speaker1.SpeakAsync("高抬腿");
        }

        private void Page_Closed(object sender, RoutedEventArgs e)
        {
            //程序结束，数据上传
            if (startTime != DateTime.MinValue)
            {
                endTime = DateTime.Now;
                TimeSpan diffTime = endTime - startTime;    //计算时间差值
                String user = Application.Current.Properties["userName"].ToString();
                String relation = Application.Current.Properties["relation"].ToString();
                //数据库操作
                MySQL_Service mySQL = new MySQL_Service();
                mySQL.ConnectSer();
                mySQL.InsertBalance(user, relation, "HighKnee", diffTime.Seconds, leftNumber, rightNumber);
                mySQL.CloseServer();
            }

            //释放资源？
            if (this.reader != null)
            {
                // ColorFrameReder is IDisposable接口
                this.reader.Dispose();
                this.reader = null;
            }
            //关闭Kinect传感器
            if (this.kinectSensor != null)
            {
                this.kinectSensor.Close();
                this.kinectSensor = null;
            }
        }

        void reader_MultiSourceFrameArrived(object sender, MultiSourceFrameArrivedEventArgs e)
        {
            var reference = e.FrameReference.AcquireFrame();
            //彩色
            using (var frame = reference.ColorFrameReference.AcquireFrame())
            {
                if (frame != null)
                {
                    if (_mode == CameraMode.Color)
                    {
                        camera.Source = frame.ToBitmap();
                    }
                }
            }
            // Body
            using (var frame = reference.BodyFrameReference.AcquireFrame())
            {
                if (frame != null)
                {
                    canvas.Children.Clear();

                    bodies = new Body[frame.BodyFrameSource.BodyCount];

                    frame.GetAndRefreshBodyData(bodies);

                    TrackOne(bodies, frame); //获取满足条件的BodyId

                    foreach (var body in bodies) //当捕获到多人骨骼点时，一个个遍历
                    {
                        //if (body.IsTracked)     //骨骼点被跟踪
                        if (body.IsTracked && (bodyId == body.TrackingId)) //骨骼点被跟踪，且相应的Id是跟踪目标
                        {
                            canvas.DrawSkeleton(body, coordinateMapper);//在canvas中绘制骨骼

                            IReadOnlyDictionary<JointType, Joint> joints = body.Joints;
                            Dictionary<JointType, Point> jointPoints = new Dictionary<JointType, Point>();
                            foreach (JointType jointType in body.Joints.Keys)
                            {
                                jointPoints[jointType] = joints[jointType].ScaleTo(coordinateMapper);
                            }
                            processGesture(body, jointPoints);      //匹配姿势

                            SwitchAction(body, jointPoints); //切换至动作切换界面

                            Knee(body);//执行高抬腿Knee函数
                        }
                    }
                }
            }
        }

        #endregion
        //检测身体动作，切换至动作切换界面
        private void SwitchAction(Body body, Dictionary<JointType, Point> jointPoints)
        {
            //Joint wristLeft = body.Joints[JointType.WristLeft];
            //Joint shoulderLeft = body.Joints[JointType.ShoulderLeft];
            Joint wristRight = body.Joints[JointType.WristRight];
            Joint shoulderRight = body.Joints[JointType.ShoulderRight];

            if (IsPose(body, this.switchPose, jointPoints) && (shoulderRight.Position.Y < wristRight.Position.Y))
            {
                speaker1.Speak("切换动作");
                SwitchBalance switchBalance = new SwitchBalance();
                switchBalance.IndexAction = 5; //设置索引值
                //this.Close(); //先关闭，后弹出switchBanlance窗口，避免switchBanlance程序中Kinect被提前关闭
                //switchBalance.Show();
                //NavigationService.GetNavigationService(this).Navigate(new Uri("/SwitchBalance.xaml", UriKind.Relative));
                this.NavigationService.Navigate(switchBalance);
            }
        }

        //获取BodyId，便于之后实现只跟踪一个人的目标 
        private void TrackOne(IList<Body> bodies, BodyFrame frame)  //只处理一人数据
        {
            //这个Id选取站立在图像中心人的bodyId。当丢失这个Id时，再次获取图像中心人的bodyId。
            //判断人是否在于中心，是利用SpineMid骨骼点的水平坐标值(X轴坐标)，判断其绝对值是否小于0.16。
            //kinect中心是X轴的原点

            int bodyNum = frame.BodyFrameSource.BodyCount; //获得图像帧中身体数目
            ArrayList bodyIds = new ArrayList(); //存放图像帧中bodyId
            int i = 0;
            float temp = 666; //临时存放水平坐标值 

            for (i = 0; i < bodyNum; i++) //记录bodyId
            {
                bodyIds.Add(bodies[i].TrackingId); //存放bodyId
            }

            if (bodyIds.Contains(bodyId)) //判断bodyId是否在该图像帧
            {

            }
            else //不在图像帧中
            {
                for (i = 0; i < bodyNum; i++)
                {
                    if (bodies[i].TrackingId > 666) //剔除无效的Id
                    {
                        temp = bodies[i].Joints[JointType.SpineMid].Position.X;

                        if ((Math.Abs(temp) < 0.16f) && bodies[i].IsTracked) //水平坐标绝对值小于阈值，且相应的身体被追踪
                        {
                            bodyId = bodies[i].TrackingId;
                            break;
                        }
                    }
                }
            }
        }


        private void Knee(Body body)
        {
            if (start)
            {
                KneeCount(body);

                //检测测量位置是否有大幅度移动
                var kneeLeft = body.Joints[JointType.KneeLeft]; //左膝盖
                var kneeRight = body.Joints[JointType.KneeRight];   //右膝盖
                if ((Math.Abs(kneeRight.Position.Z - startKneeRight.Position.Z) > 0.40f) && (Math.Abs(kneeLeft.Position.Z - startKneeLeft.Position.Z) > 0.40f))
                {
                    remind1.Text = "前后移动，测量停止";
                    speaker1.SpeakAsync("前后移动，测量停止");
                    start = false; //初始化高抬腿Knee程序标志位
                    soundStart = false; //初始化开始语音提示标志位
                }

            }
        }

        private void KneeCount(Body body)
        {
            var kneeLeft = body.Joints[JointType.KneeLeft]; //左膝盖
            var kneeRight = body.Joints[JointType.KneeRight];   //右膝盖

            //左脚抬起：Y值增大，Z值减小；右脚的Z值基本不变
            if(((kneeLeft.Position.Y - startKneeLeft.Position.Y) > 0.1f) && ((startKneeLeft.Position.Z - kneeLeft.Position.Z) > 0.15f) && (Math.Abs(kneeRight.Position.Z - startKneeRight.Position.Z) < 0.08f) && countLeft)
            {
                leftNumber += 1;
                leftNum.Text = leftNumber.ToString();
                countLeft = false;
            }

            //右脚抬起：Y值增大，Z值减小；左脚的Z值基本不变
            if (((kneeRight.Position.Y - startKneeRight.Position.Y) > 0.1f) && ((startKneeRight.Position.Z - kneeRight.Position.Z) > 0.15f) && (Math.Abs(kneeLeft.Position.Z - startKneeLeft.Position.Z) < 0.08f) && countRight)
            {
                rightNumber += 1;
                rightNum.Text = rightNumber.ToString();
                countRight = false;
            }
            
            //左右两脚站立Y轴距离与T姿势站立Y轴距离相当
            if ((Math.Abs(kneeRight.Position.Y - startKneeRight.Position.Y) < 0.03f) && (Math.Abs(kneeLeft.Position.Y - startKneeLeft.Position.Y) < 0.03f))
            {
                countLeft = true;
                countRight = true;

            }

        }

  private void GoBack_Click(object sender, RoutedEventArgs e)
        {
            NavigationService.GetNavigationService(this).Navigate(new Uri("/BalanceTrain/BalanceTrain.xaml", UriKind.Relative));
        }

    }
}
